﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Threading;

namespace UFrameWork
{
    /// <summary>
    /// 网络连接客户端
    /// </summary>
    public class UClient
    {
        /// <summary>
        /// 主机地址
        /// </summary>
        public string Host;
        
        /// <summary>
        /// 监听端口
        /// </summary>
        public int Port;

        /// <summary>
        /// 当前网络连接状态
        /// </summary>
        public bool Connected = false;

        /// <summary>
        /// tcp连接对象
        /// </summary>
        private TcpClient client;

        /// <summary>
        /// 数据传输对象
        /// </summary>
        private NetworkStream netStream;

        /// <summary>
        /// 消息结束标记
        /// </summary>
        private const string EndFlag = "\r$\n";

        /// <summary>
        /// 标记的数组数据
        /// </summary>
        private byte[] EngFlagBytes;

        /// <summary>
        /// 消息发送拼装器
        /// </summary>
        private StringBuilder sendBuilder = new StringBuilder();

        /// <summary>
        /// 数据缓存池长度
        /// </summary>
        private int dataLength = 0;

        /// <summary>
        /// 数据缓存池
        /// </summary>
        private byte[] dataCache = new byte[1024 * 1024];

        /// <summary>
        /// 读取到的数据长度
        /// </summary>
        private int receiveLength = 0;

        /// <summary>
        /// 数据接收buffer
        /// </summary>
        private byte[] receiveBuffer = new byte[1024];

        /// <summary>
        /// 网络客户端
        /// </summary>
        /// <param name="host">服务器地址</param>
        /// <param name="port">服务器端口</param>
        public UClient(string host,int port)
        {
            Host = host;
            Port = port;
            EngFlagBytes = Encoding.UTF8.GetBytes(EndFlag);
        }

        private async void Connect()
        {
            try
            {
                client = new TcpClient();
                await client.ConnectAsync(Host, Port);
                Connected = client.Connected;
            }
            catch (Exception e)
            { 
                UWork.Do(UEvent.CLIENT_CONNECT_CLOSE,this,e);
                ULog.Error(e);
            }

            if (Connected)
            {
                UWork.Do(UEvent.CLIENT_CONNECT_SUCCESS, this);
                netStream = client.GetStream();
                Receive();
            }
        }

        /// <summary>
        /// 接受数据
        /// </summary>
        private void Receive()
        {
            try
            {
                netStream.BeginRead(receiveBuffer, 0, receiveBuffer.Length, (IAsyncResult ar) =>
                {
                    receiveLength = netStream.EndRead(ar);
                    if (receiveLength > 0)
                    {
                        Array.Copy(receiveBuffer, 0, dataCache, dataLength, receiveLength);
                        UWork.Do(UEvent.CLIENT_RECEIVE_DATA, this, receiveBuffer, receiveLength);
                        dataLength += receiveLength;
                        UnPack();
                        Receive();
                    }
                }, netStream);
            }
            catch (Exception e)
            {
                Clear();
                UWork.Do(UEvent.CLIENT_CONNECT_CLOSE, this, e.Message);
                ULog.Error(e);
            }
        }

        /// <summary>
        /// 解包当前数据,并加入分发队列
        /// </summary>
        private void UnPack()
        {
            int index = GetIndexOf(dataCache, EngFlagBytes, dataLength);
            if (index == -1) return;

            string msg = Encoding.UTF8.GetString(dataCache, 0, index);

            int newLength = dataLength - index - EngFlagBytes.Length;

            Array.Copy(dataCache, index + EngFlagBytes.Length, dataCache, 0, newLength);

            UWork.Do(UEvent.CLIENT_RECEIVE_MSG, this, msg);

            Dispatch(msg);

            dataLength = newLength;

            if (dataLength > 0) UnPack();
        }

        /// <summary>
        /// 分发消息
        /// </summary>
        private void Dispatch(string msg)
        {
            UWork.Do(UEvent.CLIENT_MSG_DISPATCH, this, msg);
            try
            {
                JObject json = JsonConvert.DeserializeObject<JObject>(msg);
                string key = json["key"].Value<string>();
                List<Type> types = UWork.GetMethodParamType(key);
                List<object> paramobjs = new List<object>();
                paramobjs.Add(this);
                for (int i = 0; i < types.Count; i++)
                {
                    paramobjs.Add(json["data"][i].ToObject(types[i]));
                }
                UWork.Do(key, paramobjs.ToArray());
            }
            catch (Exception e)
            {
                UWork.Do(UEvent.CLIENT_MSG_DISPATCH_ERROR, this, msg, e);
                ULog.Error("UMsgDispatcher:" + msg + "\r\n" + e);
            }
        }

        /// <summary>
        /// 发送数据
        /// </summary>
        /// <param name="key">名称</param>
        /// <param name="obj">数据</param>
        public void Send(string key, params object[] obj)
        {
            if (!client.Connected) return;

            sendBuilder.Remove(0, sendBuilder.Length);
            sendBuilder.Append("{key:'");
            sendBuilder.Append(key);
            sendBuilder.Append("',data:");
            sendBuilder.Append(JsonConvert.SerializeObject(obj));
            sendBuilder.Append("}");
            sendBuilder.Append(EndFlag);

            UWork.Do(UEvent.CLIENT_SEND_MSG, this, sendBuilder.ToString());

            byte[] bytes = Encoding.UTF8.GetBytes(sendBuilder.ToString());

            try
            {
                netStream.Write(bytes, 0, bytes.Length);
            }
            catch (Exception e)
            {
                Clear();
                UWork.Do(UEvent.CLIENT_CONNECT_CLOSE, this, e.Message);
                ULog.Error(e);
            }
        }

        private void Clear()
        {
            Connected = false;

            if (client != null)
            {
                client.Close();
            }
        }

        /// <summary>
        /// 重新连接
        /// </summary>
        /// <param name="host">服务器地址</param>
        /// <param name="port">服务器端口</param>
        public void ReConnect(string host = "", int port = 0)
        {
            if (!string.IsNullOrEmpty(host)) Host = host;
            if (port != 0) Port = port;
            Clear();
            Connect();
        }

        /// <summary>
        /// 关闭连接
        /// </summary>
        public void Close()
        {
            Clear();
        }


        /// <summary>
        /// 在byte数组中查找byte数组的位置
        /// </summary>
        /// <param name="source">搜索数据</param>
        /// <param name="find">要的的数据</param>
        /// <param name="length">数据长度</param>
        /// <returns>-1未找到</returns>
        private int GetIndexOf(byte[] source, byte[] find, int length)
        {
            if (source == null || find == null || source.Length == 0 ||
                find.Length == 0 || source.Length < find.Length)
                return -1;

            int i, j;
            for (i = 0; i < length - find.Length + 1; i++)
            {
                if (source[i] == find[0])
                {
                    for (j = 1; j < find.Length; j++)
                    {
                        if (source[i + j] != find[j])
                            break;
                    }
                    if (j == find.Length)
                        return i;
                }
            }
            return -1;
        }
    }
}